package helper

import (
	"fmt"
	"strings"
	// "errors"

	"mall/service/wpgql/api/internal/graph/model"
	"mall/service/wpgql/api/internal/graph/util"
)

const (
	orderBySeparator = ","
)

func GetForumPostConnectionSorterDirection(sorter []*model.ForumPostConnectionSorter) util.SortDirection {
	if sorter != nil && len(sorter) > 0 {
		for _, s := range sorter {
			return util.SortDirection(s.Order)
		}
	}

	return util.SortDirectionAsc
}

func GetForumPostConnectionOrderBy(
	sorter []*model.ForumPostConnectionSorter,
	reverse bool,
	defaultDirection util.SortDirection,
) string {
	var a []string
	entityMap, hasEntityMap := util.RpcSorterFieldMap[util.RpcEntityNames.ForumPost]
	if hasEntityMap {
		var handledID bool
		for _, s := range sorter {
			if s.Field == model.ForumPostConnectionOrderbyEnumID {
				handledID = true
			}
			column, hasColumn := entityMap[string(s.Field)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(util.SortDirection(s.Order), reverse),
				))
			}
		}

		if !handledID {
			column, hasColumn := entityMap[string(model.ForumPostConnectionOrderbyEnumID)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(defaultDirection, reverse),
				))
			}
		}
	}

	return strings.Join(a, orderBySeparator)
}

func GetForumPostConnectionSorterMap(
	pagination *util.ConnectionPagination,
	sorter []*model.ForumPostConnectionSorter,
) map[string]string {
	if sorter == nil || len(sorter) == 0 {
		return nil
	}

	sorterMap := make(map[string]string)

	limit, cursor, reverse := util.GetPageInfo(pagination)

	offset := util.GetOffsetFromCursor(cursor)

	direction := GetForumPostConnectionSorterDirection(sorter)
	orderBy := GetForumPostConnectionOrderBy(sorter, reverse, direction)

	sorterMap["offset"]  = fmt.Sprintf("%v", offset)
	sorterMap["limit"]   = fmt.Sprintf("%v", limit)
	sorterMap["reverse"] = fmt.Sprintf("%v", reverse)
	sorterMap["sorter"]  = fmt.Sprintf("%v", orderBy)

	return sorterMap
}

func ForumPostConnectionEdgeConverter(
	offset int,
) func(*model.ForumPost, int) *model.ForumPostConnectionEdge {
	return func(m *model.ForumPost, i int) *model.ForumPostConnectionEdge {
		return &model.ForumPostConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetForumPostStartEndCursor(edges []*model.ForumPostConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetForumPostConnection(
	posts []*model.ForumPost,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	limit int32,
	reverse bool,
) *model.ForumPostConnection {
	edges := make([]*model.ForumPostConnectionEdge, 0, len(posts))
	edgeConverter := ForumPostConnectionEdgeConverter(int(offset))
	util.BaseConnection(len(posts), reverse, func(i int) {
		edges = append(edges, edgeConverter(posts[i], i))
	})
	startCursor, endCursor := GetForumPostStartEndCursor(edges)
	return &model.ForumPostConnection{
		Edges:    edges,
		PageInfo: &model.WPPageInfo{
			EndCursor:       endCursor,
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
			StartCursor:     startCursor,
		},
	}
}

func GetForumReactionConnectionSorterDirection(sorter []*model.ForumReactionConnectionSorter) util.SortDirection {
	if sorter != nil && len(sorter) > 0 {
		for _, s := range sorter {
			return util.SortDirection(s.Order)
		}
	}

	return util.SortDirectionAsc
}

func GetForumReactionConnectionOrderBy(
	sorter []*model.ForumReactionConnectionSorter,
	reverse bool,
	defaultDirection util.SortDirection,
) string {
	var a []string
	entityMap, hasEntityMap := util.RpcSorterFieldMap[util.RpcEntityNames.ForumReaction]
	if hasEntityMap {
		var handledID bool
		for _, s := range sorter {
			if s.Field == model.ForumReactionConnectionOrderbyEnumID {
				handledID = true
			}
			column, hasColumn := entityMap[string(s.Field)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(util.SortDirection(s.Order), reverse),
				))
			}
		}

		if !handledID {
			column, hasColumn := entityMap[string(model.ForumReactionConnectionOrderbyEnumID)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(defaultDirection, reverse),
				))
			}
		}
	}

	return strings.Join(a, orderBySeparator)
}

func GetForumReactionConnectionSorterMap(
	pagination *util.ConnectionPagination,
	sorter []*model.ForumReactionConnectionSorter,
) map[string]string {
	if sorter == nil || len(sorter) == 0 {
		return nil
	}

	sorterMap := make(map[string]string)

	limit, cursor, reverse := util.GetPageInfo(pagination)

	offset := util.GetOffsetFromCursor(cursor)

	direction := GetForumReactionConnectionSorterDirection(sorter)
	orderBy := GetForumReactionConnectionOrderBy(sorter, reverse, direction)

	sorterMap["offset"]  = fmt.Sprintf("%v", offset)
	sorterMap["limit"]   = fmt.Sprintf("%v", limit)
	sorterMap["reverse"] = fmt.Sprintf("%v", reverse)
	sorterMap["sorter"]  = fmt.Sprintf("%v", orderBy)

	return sorterMap
}

func ForumReactionConnectionEdgeConverter(
	offset int,
) func(*model.ForumReaction, int) *model.ForumReactionConnectionEdge {
	return func(m *model.ForumReaction, i int) *model.ForumReactionConnectionEdge {
		return &model.ForumReactionConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetForumReactionStartEndCursor(edges []*model.ForumReactionConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetForumReactionConnection(
	reactions []*model.ForumReaction,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	limit int32,
	reverse bool,
) *model.ForumReactionConnection {
	edges := make([]*model.ForumReactionConnectionEdge, 0, len(reactions))
	edgeConverter := ForumReactionConnectionEdgeConverter(int(offset))
	util.BaseConnection(len(reactions), reverse, func(i int) {
		edges = append(edges, edgeConverter(reactions[i], i))
	})
	startCursor, endCursor := GetForumReactionStartEndCursor(edges)
	return &model.ForumReactionConnection{
		Edges:    edges,
		PageInfo: &model.WPPageInfo{
			EndCursor:       endCursor,
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
			StartCursor:     startCursor,
		},
	}
}

func GetForumUserConnectionSorterDirection(sorter []*model.ForumUserConnectionSorter) util.SortDirection { //// 第一项的order作为默认方向
	if sorter != nil && len(sorter) > 0 {
		for _, s := range sorter {
			return util.SortDirection(s.Order)
		}
	}

	return util.SortDirectionAsc
}

func GetForumUserCursorType(sorter []*model.ForumUserConnectionSorter) util.CursorType { //// mysql不适用
	countDirection, result := util.CursorTypeCounter()
	for _, s := range sorter {
		countDirection(util.SortDirection(s.Order))
	}

	return result()
}

func ForumUserConnectionSorterFieldValueFromCursorValue(cursorValue string) (string, interface{}) { //// mysql不适用 从游标数组项解析Sorter Field及其值
	key, value := util.FromCursorValue(cursorValue)
	entityMap, hasEntityMap := util.RpcSorterFieldMap[util.RpcEntityNames.ForumUser]
	if !hasEntityMap {
		return "", nil
	}

	column, hasColumn := entityMap[key]
	if !hasColumn {
		return "", nil
	}

	if model.ForumUserConnectionOrderbyEnum(key) == model.ForumUserConnectionOrderbyEnumID { /// ID生成游标时前面附加rpcEntityName
		return column, util.GetIDFromCursor(value)
	}

	return column, util.StringToInterface(value)
}

func GetForumUserConnectionOrderBy(
	sorter []*model.ForumUserConnectionSorter,
	reverse bool,
	defaultDirection util.SortDirection,
) string { //// 组装order by子句，附加"ID ASC/DESC"
	var a []string
	entityMap, hasEntityMap := util.RpcSorterFieldMap[util.RpcEntityNames.ForumUser]
	if hasEntityMap {
		var handledID bool
		for _, s := range sorter {
			if s.Field == model.ForumUserConnectionOrderbyEnumID {
				handledID = true
			}
			column, hasColumn := entityMap[string(s.Field)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(util.SortDirection(s.Order), reverse),
				))
			}
		}

		if !handledID {
			column, hasColumn := entityMap[string(model.ForumUserConnectionOrderbyEnumID)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(defaultDirection, reverse),
				))
			}
		}
	}

	return strings.Join(a, orderBySeparator)
}

func GetForumUserConnectionSorterMap(
	pagination *util.ConnectionPagination,
	sorter []*model.ForumUserConnectionSorter,
) map[string]string { ////
	if sorter == nil || len(sorter) == 0 {
		return nil
	}

	sorterMap := make(map[string]string)

	// if pagination.Forward != nil && pagination.Backward != nil {
	// 	return nil, errors.New("can not use forward and backward pagination at once")
	// }
	// if pagination.Forward == nil && pagination.Backward == nil {
	// 	return nil, errors.New("no forward or backward pagination provided")
	// }

	limit, cursor, reverse := util.GetPageInfo(pagination)

	offset := util.GetOffsetFromCursor(cursor)

	direction := GetForumUserConnectionSorterDirection(sorter)
	orderBy := GetForumUserConnectionOrderBy(sorter, reverse, direction)

	sorterMap["offset"]  = fmt.Sprintf("%v", offset)
	sorterMap["limit"]   = fmt.Sprintf("%v", limit)
	sorterMap["reverse"] = fmt.Sprintf("%v", reverse)
	sorterMap["sorter"]  = fmt.Sprintf("%v", orderBy)

	return sorterMap
}

func ForumUserConnectionEdgeConverter(
	offset int,
) func(*model.ForumUser, int) *model.ForumUserConnectionEdge { ////
	return func(m *model.ForumUser, i int) *model.ForumUserConnectionEdge {
		return &model.ForumUserConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetForumUserStartEndCursor(edges []*model.ForumUserConnectionEdge) (*string, *string) { ////
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor   = c
	}

	return startCursor, endCursor
}

func GetForumUserConnection(
	users []*model.ForumUser,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	limit int32,
	reverse bool,
) *model.ForumUserConnection { ////
	edges := make([]*model.ForumUserConnectionEdge, 0, len(users))
	edgeConverter := ForumUserConnectionEdgeConverter(int(offset))
	util.BaseConnection(len(users), reverse, func(i int) { // 组装edges（reverse意味着反向组装）
		edges = append(edges, edgeConverter(users[i], i))
	})
	startCursor, endCursor := GetForumUserStartEndCursor(edges)
	return &model.ForumUserConnection{
		Edges:    edges,
		PageInfo: &model.WPPageInfo{
			EndCursor:       endCursor,
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
			StartCursor:     startCursor,
		},
	}
}
